/*
 * OrientationMatrix.h
 *
 * Created 6/1/2009 By Johnny Huynh
 *
 * Version 00.00.02 7/6/2009
 *
 * Copyright Information:
 * All content copyright © 2009 Johnny Huynh. All rights reserved.
 */
 
 // OrientationMatrix.h contains functions for matrices that represent orientations

 #ifndef ORIENTATIONMATRIX_H
 #define ORIENTATIONMATRIX_H
 
 #include "macro.h"
 #include "OrthogonalMatrix.h"
 #include "Vector3.h"
 #include "OpenGL_Headers.h"
 #include <math.h>

 ///////////////////////////////// MATRIX 3 x 3 //////////////////////////////////////
 
 template <typename TYPENAME> class OrientationMatrix3;
 
 typedef OrientationMatrix3<GLfloat> OrientationMatrix3f;
 typedef OrientationMatrix3<GLdouble> OrientationMatrix3d;
 
 // The matrix based on the row and column
 //
 // | Xx Yx Zx |
 // | Xy Yy Zy |
 // | Xz Yz Zz |
 
 // Counter-clockwise rotation matrix
 //
 // | cos( radian )     -sin( radian ) |
 // | sin( radian )      cos( radian ) |
 
 template <typename TYPENAME>
 class OrientationMatrix3 : public OrthogonalMatrix3<TYPENAME>
 {
 // Data Members
 protected:
 
 // Local Functions
 public:
    OrientationMatrix3( const TYPENAME& mXx = ZERO, const TYPENAME& mYx = ZERO, const TYPENAME& mZx = ZERO,
                        const TYPENAME& mXy = ZERO, const TYPENAME& mYy = ZERO, const TYPENAME& mZy = ZERO,
                        const TYPENAME& mXz = ZERO, const TYPENAME& mYz = ZERO, const TYPENAME& mZz = ZERO );
    OrientationMatrix3( const OrthogonalMatrix3<TYPENAME>& m );
    OrientationMatrix3( const Matrix3<TYPENAME>& m );
    
    #ifdef VECTOR3_H
    OrientationMatrix3( const Vector3<TYPENAME>& u, const Vector3<TYPENAME>& v, const Vector3<TYPENAME>& t );
    #endif // VECTOR3_H
    
    ~OrientationMatrix3();
    
    inline Vector3<TYPENAME> getXAxis() const;
	inline Vector3<TYPENAME> getYAxis() const;
	inline Vector3<TYPENAME> getZAxis() const;
    inline OrientationMatrix3<TYPENAME>& globalRotateZX( TYPENAME radian );
    inline OrientationMatrix3<TYPENAME>& globalRotateZY( TYPENAME radian );
    inline OrientationMatrix3<TYPENAME>& globalRotateXY( TYPENAME radian );
    inline OrientationMatrix3<TYPENAME>& localRotateZX( TYPENAME radian );
    inline OrientationMatrix3<TYPENAME>& localRotateZY( TYPENAME radian );
    inline OrientationMatrix3<TYPENAME>& localRotateXY( TYPENAME radian );
    
 // Private Functions
 private:
 
 // Friend Functions
 public:

 };



 /** LOCAL FUNCTIONS **/
 
 /**
  * Constructor
  */
 template <typename TYPENAME>
 OrientationMatrix3<TYPENAME>::OrientationMatrix3( 
                             const TYPENAME& mXx = ZERO, const TYPENAME& mYx = ZERO, const TYPENAME& mZx = ZERO,
                             const TYPENAME& mXy = ZERO, const TYPENAME& mYy = ZERO, const TYPENAME& mZy = ZERO,
                             const TYPENAME& mXz = ZERO, const TYPENAME& mYz = ZERO, const TYPENAME& mZz = ZERO )
                           : OrthogonalMatrix3<TYPENAME>( mXx, mXy, mXz,
                                                          mYx, mYy, mYz,
                                                          mZx, mZy, mZz )
 {

 }
 
 /**
  * Alternative Constructor
  */
 template <typename TYPENAME>
 OrientationMatrix3<TYPENAME>::OrientationMatrix3( const OrthogonalMatrix3<TYPENAME>& m )
                           : OrthogonalMatrix3<TYPENAME>( m )
 {

 }
 
 /**
  * Alternative Constructor
  */
 template <typename TYPENAME>
 OrientationMatrix3<TYPENAME>::OrientationMatrix3( const Matrix3<TYPENAME>& m )
                           : OrthogonalMatrix3<TYPENAME>( m )
 {

 }
 
 #ifdef VECTOR3_H
 /**
  * Alternative Constructor
  *
  *     [u.x v.x t.x
  * M =  u.y v.y t.y
  *      u.z v.z t.z]
  */
 template <typename TYPENAME>
 OrientationMatrix3<TYPENAME>::OrientationMatrix3( 
                            const Vector3<TYPENAME>& u, const Vector3<TYPENAME>& v, const Vector3<TYPENAME>& t )
                           : OrthogonalMatrix3<TYPENAME>( u, v, t )
 {

 }
 #endif // VECTOR3_H
 
 /**
  * Destructor
  */
 template <typename TYPENAME>
 OrientationMatrix3<TYPENAME>::~OrientationMatrix3()
 {

 }
 
 /**
  * getXAxis() returns the local x-axis of this
  * orientation as a unit vector.
  *
  * @return Vector3<TYPENAME>
  */
 template <typename TYPENAME>
 inline Vector3<TYPENAME> OrientationMatrix3<TYPENAME>::getXAxis() const
 {
    return Vector3<TYPENAME>( Xx, Xy, Xz );
 }
 
 /**
  * getYAxis() returns the local y-axis of this
  * orientation as a unit vector.
  *
  * @return Vector3<TYPENAME>
  */
 template <typename TYPENAME>
 inline Vector3<TYPENAME> OrientationMatrix3<TYPENAME>::getYAxis() const
 {
    return Vector3<TYPENAME>( Yx, Yy, Yz );
 }
 
 /**
  * getZAxis() returns the local z-axis of this
  * orientation as a unit vector.
  *
  * @return Vector3<TYPENAME>
  */
 template <typename TYPENAME>
 inline Vector3<TYPENAME> OrientationMatrix3<TYPENAME>::getZAxis() const
 {
    return Vector3<TYPENAME>( Zx, Zy, Zz );
 }

 /**
  * globalRotateZX() rotates the orientation from the global z-axis towards the global x-axis
  * (i.e. about the global y-axis).
  *
  *      x ^   
  *        |
  *  -z <-- --> z
  *        |
  *     -x v
  *
  * @param (TYPENAME) radian
  */
 template <typename TYPENAME>
 inline OrientationMatrix3<TYPENAME>& OrientationMatrix3<TYPENAME>::globalRotateZX( TYPENAME radian )
 {
    // Keep rotating from the z-axis towards the x-axis as if the y-axis is positive, even when the view is upside-down
    if (Yy < ZERO)
        radian = -radian;
        
    // [RotationZX Matrix]:
    // cos( radian ),  0.0f, sin( radian ),
    // 0.0f,           1.0f, 0.0f,
    // -sin( radian ), 0.0f, cos( radian )
    
    // [RotationZX Matrix] * x-axis
    // [RotationZX Matrix] * y-axis
    // [RotationZX Matrix] * z-axis
    
    TYPENAME sine( sin( radian ) );
    TYPENAME cosine( cos( radian ) );

    // z-axis
    TYPENAME a( (Zx * cosine) + (Zz * sine) ); // Zx
    TYPENAME b( (Zz * cosine) - (Zx * sine) ); // Zz
    Zx = a;
    Zz = b;

    // y-axis
    a = (Yx * cosine) + (Yz * sine); // Yx
    b = (Yz * cosine) - (Yx * sine); // Yz
    Yx = a;
    Yz = b;

    // x-axis
    a = (Xx * cosine) + (Xz * sine); // Xx
    b = (Xz * cosine) - (Xx * sine); // Xz
    Xx = a;
    Xz = b;

    maintain( *this );
    
    return *this;
 }
 
 /**
  * globalRotateZY() rotates the orientation from the global z-axis towards the global y-axis
  * (i.e. about the global x-axis).
  *
  *      y ^   
  *        |
  *  -z <-- --> z
  *        |
  *     -y v
  *
  * @param (TYPENAME) radian
  */
 template <typename TYPENAME>
 inline OrientationMatrix3<TYPENAME>& OrientationMatrix3<TYPENAME>::globalRotateZY( TYPENAME radian )
 {
    // Keep rotating from the z-axis towards the y-axis as if the x-axis is positive, even when the view is turned-around
    if (Xx < ZERO)
        radian = -radian;

    // [RotationZY Matrix]:
    // 1.0f,  0.0f,          0.0f,
    // 0.0f,  cos( radian ), sin( radian ),
    // 0.0f, -sin( radian ), cos( radian )
    
    // [RotationZY Matrix] * x-axis
    // [RotationZY Matrix] * y-axis
    // [RotationZY Matrix] * z-axis
    
    TYPENAME sine( sin( radian ) );
    TYPENAME cosine( cos( radian ) );

    // z-axis
    TYPENAME a( (Zz * cosine) - (Zy * sine) ); // Zz
    TYPENAME b( (Zz * sine) + (Zy * cosine) ); // Zy
    Zz = a;
    Zy = b;

    // y-axis
    a = (Yz * cosine) - (Yy * sine); // Yz
    b = (Yz * sine) + (Yy * cosine); // Yy
    Yz = a;
    Yy = b;

    // x-axis
    a = (Xz * cosine) - (Xy * sine); // Xz
    b = (Xz * sine) + (Xy * cosine); // Xy
    Xz = a;
    Xy = b;

    maintain( *this );
    
    return *this;
 }
 
 /**
  * globalRotateXY() rotates the orientation from the global x-axis towards the global y-axis 
  * (i.e. about the global z-axis).
  *
  *      y ^   
  *        |
  *  -x <-- --> x
  *        |
  *     -y v
  *
  * @param (GLfloat)radian
  */
 template <typename TYPENAME>
 inline OrientationMatrix3<TYPENAME>& OrientationMatrix3<TYPENAME>::globalRotateXY( TYPENAME radian )
 {
    // Keep rotating from the x-axis towards the y-axis as if the z-axis is positive, even when the view is turned-around
    if (Zz < ZERO)
        radian = -radian;
    
    // [RotationXY Matrix]:
    // cos( radian ), -sin( radian ), 0.0f,
    // sin( radian ),  cos( radian ), 0.0f,
    // 0.0f,           0.0f,          1.0f 
    
    // [RotationXY Matrix] * x-axis
    // [RotationXY Matrix] * y-axis
    // [RotationXY Matrix] * z-axis
    
    TYPENAME sine( sin( radian ) );
    TYPENAME cosine( cos( radian ) );
    
    // z-axis
    TYPENAME a( (Zx * cosine) - (Zy * sine) ); // Zx
    TYPENAME b( (Zx * sine) + (Zy * cosine) ); // Zy
    Zx = a;
    Zy = b;

    // y-axis
    a = (Yx * cosine) - (Yy * sine); // Yx
    b = (Yx * sine) + (Yy * cosine); // Yy
    Yx = a;
    Yy = b;

    // x-axis
    a = (Xx * cosine) - (Xy * sine); // Xx
    b = (Xx * sine) + (Xy * cosine); // Xy
    Xx = a;
    Xy = b;

    maintain( *this );
    
    return *this;
 }
 
 /**
  * localRotateZX() rotates the orientation from the local z-axis towards the local x-axis 
  * (i.e. about the local y-axis).
  *
  * @param (GLfloat)radian
  */
 template <typename TYPENAME>
 inline OrientationMatrix3<TYPENAME>& OrientationMatrix3<TYPENAME>::localRotateZX( TYPENAME radian )
 {
    // T = current matrix
    // T^-1 = inverse of T
    // R = global rotation matrix
    // I = identity matrix
    // T R T^-1 T = T R I
    
    // The implementation rotates the current local orientation back to
    // its global orientation (which corresponds with the identity matrix).
    // T^-1 T = I
    
    // R = [RotationZX Matrix]:
    // cos( radian ),  0.0f, sin( radian ),
    // 0.0f,           1.0f, 0.0f,
    // -sin( radian ), 0.0f, cos( radian )
    
    TYPENAME sine( sin( radian ) );
    TYPENAME cosine( cos( radian ) );
    
    TYPENAME a( (Xx * cosine) - (Zx * sine) ); // Xx
    TYPENAME b( (Xx * sine) + (Zx * cosine) ); // Zx
    Xx = a;
    Zx = b;
    
    a = (Xy * cosine) - (Zy * sine); // Xy
    b = (Xy * sine) + (Zy * cosine); // Zy
    Xy = a;
    Zy = b;
    
    a = (Xz * cosine) - (Zz * sine); // Xz
    b = (Xz * sine) + (Zz * cosine); // Zz
    Xz = a;
    Zz = b;
    
    maintain( *this );
    
    return *this;
 }
 
 /**
  * localRotateZY() rotates the orientation from the local z-axis towards the local y-axis
  * (i.e. about the local x-axis).
  *
  * @param (GLfloat)radian
  */
 template <typename TYPENAME>
 inline OrientationMatrix3<TYPENAME>& OrientationMatrix3<TYPENAME>::localRotateZY( TYPENAME radian )
 {
    // T = current matrix
    // T^-1 = inverse of T
    // R = global rotation matrix
    // I = identity matrix
    // T R T^-1 T = T R I
    
    // The implementation rotates the current local orientation back to
    // its global orientation (which corresponds with the identity matrix).
    // T^-1 T = I
    
    // R = [RotationZY Matrix]:
    // 1.0f,  0.0f,          0.0f,
    // 0.0f,  cos( radian ), sin( radian ),
    // 0.0f, -sin( radian ), cos( radian )
    
    TYPENAME sine( sin( radian ) );
    TYPENAME cosine( cos( radian ) );
    
    TYPENAME a( (Yx * cosine) - (Zx * sine) ); // Yx
    TYPENAME b( (Yx * sine) + (Zx * cosine) ); // Zx
    Yx = a;
    Zx = b;
    
    a = (Yy * cosine) - (Zy * sine); // Yy
    b = (Yy * sine) + (Zy * cosine); // Zy
    Yy = a;
    Zy = b;
    
    a = (Yz * cosine) - (Zz * sine); // Yz
    b = (Yz * sine) + (Zz * cosine); // Zz
    Yz = a;
    Zz = b;
    
    maintain( *this );
    
    return *this;
 }
 
 /**
  * localRotateXY() rotates the orientation from the local x-axis towards the local y-axis 
  * (i.e. about the local z-axis).
  *
  * @param (GLfloat)radian
  */
 template <typename TYPENAME>
 inline OrientationMatrix3<TYPENAME>& OrientationMatrix3<TYPENAME>::localRotateXY( TYPENAME radian )
 {
    // T = current matrix
    // T^-1 = inverse of T
    // R = global rotation matrix
    // I = identity matrix
    // T R T^-1 T = T R I
    
    // The implementation rotates the current local orientation back to
    // its global orientation (which corresponds with the identity matrix).
    // T^-1 T = I
    
    // R = [RotationXY Matrix]:
    // cos( radian ), -sin( radian ), 0.0f,
    // sin( radian ),  cos( radian ), 0.0f,
    // 0.0f,           0.0f,          1.0f 
    
    TYPENAME sine( sin( radian ) );
    TYPENAME cosine( cos( radian ) );
    
    TYPENAME a( (Xx * cosine) + (Yx * sine) ); // Xx
    TYPENAME b( (Yx * cosine) - (Xx * sine) ); // Yx
    Xx = a;
    Yx = b;
    
    a = (Xy * cosine) + (Yy * sine); // Xy
    b = (Yy * cosine) - (Xy * sine); // Yy
    Xy = a;
    Yy = b;
    
    a = (Xz * cosine) + (Yz * sine); // Xz
    b = (Yz * cosine) - (Xz * sine); // Yz
    Xz = a;
    Yz = b;
    
    maintain( *this );
    
    return *this;
 }
 
 #endif // ORTHOGONALMATRIX_H